package core

import (
	"fmt"
	"reflect"
	"strings"
)

// Sequence Text Tag RuleGroup
type SeqTextTagParserStruct struct {
	rules []BaseRule
	lines []string
}

func SeqTextTagParser(ctx *Context) *SeqTextTagParserStruct {
	lines := ctx.Get("lines").([]string)
	return &SeqTextTagParserStruct{
		lines:   lines,
	}
}

func(parser *SeqTextTagParserStruct) AddRule(r...BaseRule) *SeqTextTagParserStruct{
	parser.rules = append(parser.rules, r...)
	return parser
}

type Label struct {
	tag  string
	line string
}

func(label Label) String() string{
	return fmt.Sprintf("%s %s", label.tag, label.line)
}

func(label Label) GetTag() string{
	return label.tag
}

func(label Label) GetLine() string{
	return label.line
}

func(parser *SeqTextTagParserStruct) Parse(ctx *Context){
	parserRuleIndex := 0
	parserLineIndex := 0
	labels := []Label{}
	for {

		start: if parserLineIndex >= len(parser.lines){
			break
		}

		line := parser.lines[parserLineIndex]
		ctx.Set("line", line)

		ruleIndex := parserRuleIndex
		for {

			if ruleIndex >= len(parser.rules){
				break
			}

			rule := parser.rules[ruleIndex]
			if rule.Condition(ctx){
				rule.Action(ctx)
				tag := reflect.ValueOf(rule).Elem().FieldByName("Tag").String()
				labels = append(labels, Label{tag: tag, line: line})
				parserLineIndex += 1
				parserRuleIndex = ruleIndex + 1
				goto start
			}else{
				ruleIndex += 1
			}
		}

		// current line belong pre rule
		if parserRuleIndex-1 >= 0{
			rule := parser.rules[parserRuleIndex-1]
			rule.Action(ctx)
			tag := reflect.ValueOf(rule).Elem().FieldByName("Tag").String()
			labels = append(labels, Label{tag: tag, line: line})
			parserLineIndex += 1
		}
	}

	labelList := []map[string]string{}
	for _, label := range labels{
		labelList = append(labelList, map[string]string{
			"tag": label.GetTag(),
			"line": label.GetLine(),
		})
	}

	if len(labelList) == 0{
		tag := reflect.ValueOf(parser.rules[0]).Elem().FieldByName("Tag").String()
		labelList = append(labelList, map[string]string{
			"tag": tag,
			"line": strings.Join(ctx.Get("lines").([]string), "\n"),
		})
	}

	ctx.Set("labels", labelList)
}
